// @hidden

import React, { useCallback, useEffect, useRef, useState } from "react";
import { cn } from "~/lib/utils";

interface ScrollState {
  isAtBottom: boolean;
  autoScrollEnabled: boolean;
}

interface UseAutoScrollOptions {
  offset?: number;
  smooth?: boolean;
  content?: React.ReactNode;
}

export function useAutoScroll(options: UseAutoScrollOptions = {}) {
  const { offset = 20, smooth = false, content } = options;
  const scrollRef = useRef<HTMLDivElement>(null);
  const lastContentHeight = useRef(0);
  const userHasScrolled = useRef(false);

  const [scrollState, setScrollState] = useState<ScrollState>({
    isAtBottom: false,
    autoScrollEnabled: true,
  });

  const checkIsAtBottom = useCallback(
    (element: HTMLElement) => {
      const { scrollTop, scrollHeight, clientHeight } = element;
      const distanceToBottom = Math.abs(
        scrollHeight - scrollTop - clientHeight,
      );
      return distanceToBottom <= offset;
    },
    [offset],
  );

  const scrollToBottom = useCallback(
    (instant?: boolean) => {
      if (scrollRef.current) {
        const targetScrollTop =
          scrollRef.current.scrollHeight - scrollRef.current.clientHeight;

        if (instant) {
          scrollRef.current.scrollTop = targetScrollTop;
        } else {
          scrollRef.current.scrollTo({
            top: targetScrollTop,
            behavior: smooth ? "smooth" : "auto",
          });
        }

        setScrollState({
          isAtBottom: true,
          autoScrollEnabled: true,
        });
        userHasScrolled.current = false;
      }
    },
    [smooth],
  );

  const handleScroll = useCallback(() => {
    if (scrollRef.current) {
      const atBottom = checkIsAtBottom(scrollRef.current);

      setScrollState((prev) => ({
        isAtBottom: atBottom,
        // Re-enable auto-scroll if at the bottom
        autoScrollEnabled: atBottom ? true : prev.autoScrollEnabled,
      }));
    }
  }, [checkIsAtBottom]);

  useEffect(() => {
    const element = scrollRef.current;
    if (element) {
      element.addEventListener("scroll", handleScroll, { passive: true });
    }

    return () =>
      element ? element.removeEventListener("scroll", handleScroll) : undefined;
  }, [handleScroll]);

  useEffect(() => {
    const scrollElement = scrollRef.current;
    if (!scrollElement) {
      return;
    }

    const currentHeight = scrollElement.scrollHeight;
    const hasNewContent = currentHeight !== lastContentHeight.current;

    if (hasNewContent) {
      if (scrollState.autoScrollEnabled) {
        requestAnimationFrame(() => {
          scrollToBottom(lastContentHeight.current === 0);
        });
      }
      lastContentHeight.current = currentHeight;
    }
  }, [content, scrollState.autoScrollEnabled, scrollToBottom]);

  useEffect(() => {
    const resizeObserver = new ResizeObserver(() => {
      if (scrollState.autoScrollEnabled) {
        scrollToBottom(true);
      }
    });

    const element = scrollRef.current;
    if (element) {
      resizeObserver.observe(element);
    }

    return () => resizeObserver.disconnect();
  }, [scrollState.autoScrollEnabled, scrollToBottom]);

  const disableAutoScroll = useCallback(() => {
    const atBottom = scrollRef.current
      ? checkIsAtBottom(scrollRef.current)
      : false;

    // Only disable if not at bottom
    if (!atBottom) {
      userHasScrolled.current = true;
      setScrollState((prev) => ({
        ...prev,
        autoScrollEnabled: false,
      }));
    }
  }, [checkIsAtBottom]);

  return {
    scrollRef,
    isAtBottom: scrollState.isAtBottom,
    autoScrollEnabled: scrollState.autoScrollEnabled,
    scrollToBottom: () => scrollToBottom(false),
    disableAutoScroll,
  };
}

export const ScrollAreaWithAutoScroll = ({
  children,
  className,
}: {
  children: React.ReactNode;
  className?: string;
}) => {
  const { scrollRef } = useAutoScroll({
    smooth: false,
    content: children,
  });

  return (
    <div
      ref={scrollRef}
      className={cn(
        "flex grow flex-col items-center overflow-y-auto",
        className,
      )}
    >
      <div className="flex h-full w-full max-w-[80ch] flex-col pb-4">
        {children}
      </div>
    </div>
  );
};
